package top.xizai.anti.replay.utils;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * @NAME: WSC
 * @DATE: 2021/12/16
 * @DESCRIBE:
 **/
@Slf4j
@Component
public class SignUtil {
    // 密钥
    private static final String SECRET = "xxxxxxxxxxxxxxxxxxxxxxx";
    private static final String SIGN = "sign";
    private static final String NONCE = "nonce";
    private static final String TIMESTAMP = "timestampStr";
    private static final String SIGN_KEY = "apisign_";

    @Autowired
    RedisUtils redisUtils;

    /**
     * 生成
     * @param params
     * @return
     */
    private String genSign(TreeMap<String, Object> params) {
        params.remove(SIGN);
        StringBuilder str = new StringBuilder();
        for (String key : params.keySet()) {
            Object val = params.get(key);
            if (ObjectUtil.isNotNull(val)) {
                // 1 拼接参数
                str.append(key).append(val);
            }
        }
        // 2 拼接秘钥
        str.append(SECRET);
        // 3 MD5加密
        return SecureUtil.md5(str.toString());
    }


    public  void validateSign(Map<String, Object> params) {

        var copiedMap = new HashMap<String, Object>();
        copiedMap.putAll(params);
        /**
         * 将data的所有字段放在params里
         */
        String data =  copiedMap.get("data") + "";
        if (data != null) {
            copiedMap.putAll((Map<? extends String, ?>) JSON.parse(data));
            copiedMap.remove("data");
        }

        String sign = (String) copiedMap.get(SIGN);
        if (StringUtils.isNotBlank(sign)) {
            if (StringUtils.isBlank(sign)) {
                throw new RuntimeException("签名不能为空");
            }

            String nonce = (String) copiedMap.get(NONCE);
            if (StringUtils.isBlank(nonce)) {
                throw new RuntimeException("随机字符串不能为空");
            }

            String timestampStr = (String) copiedMap.get(TIMESTAMP);
            if (StringUtils.isBlank(timestampStr)) {
                throw new RuntimeException("时间戳不能为空");
            }

            long timestamp = 0;
            try {
                timestamp = Long.parseLong(timestampStr);
            } catch (Exception e) {
                log.error("发生异常",e);
            }
            // 请求传过来的时间戳与服务器当前时间戳差值大于120，则当前请求的timestamp无效
            if (Math.abs(timestamp - System.currentTimeMillis() / 1000) > 120) {
                throw new RuntimeException("签名已过期");
            }

            // 请求传过来的随机数如果在redis中存在，则当前请求的nonce无效
            boolean nonceExists = redisUtils.hasKey(SIGN_KEY + timestampStr + nonce);
            if (nonceExists) {
                throw new RuntimeException("随机字符串已存在");
            }

            // 根据请求传过来的参数构造签名，如果和接口的签名不一致，则请求参数被篡改
            TreeMap<String, Object> signTreeMap = new TreeMap<>();
            signTreeMap.putAll(copiedMap);
            String currentSign = genSign(signTreeMap);
            if (!sign.equalsIgnoreCase(currentSign)) {
                throw new RuntimeException("签名不匹配");
            }

            // 存入redis
            redisUtils.set(SIGN_KEY + timestampStr+ nonce, nonce);
            redisUtils.expire(SIGN_KEY + timestampStr+ nonce, 120L);
        }

    }

}
